package com.unbiz.common.plugin;

import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;

import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;


import com.unbiz.common.lang.BaseResource;
import com.unbiz.common.logger.LoggerProvider;


/**
 * 其实是PluginFactory的Manager
 * 将插件打包成jar文件放到plugins文件夹下
 * @author Hunteron-cp
 * 每个接口的pluginFactory有自己的manager
 * @param <T>
 */
public abstract class PluginManager<T> implements LoggerProvider{
	protected static Map<Type,PluginManager<?>> managers = new HashMap<>();
	protected static FileAlterationMonitor fileMonitor = null;
	
	// 插件列表
	protected Map<String,PluginFactory<T>> pluginFactories = new LinkedHashMap<>();	
	
	public PluginManager(){
		Class<?> clsz = this.getClass();	
		Type t = clsz.getGenericSuperclass();		
		Type[] params = ((ParameterizedType) t).getActualTypeArguments();
		Type ncls =  params[0];		
		managers.put(ncls, this);
		
		init();
	}

	public abstract void init();
	
	public Map<String,PluginFactory<T>> getPluginFactories(){
		return pluginFactories;
	}
	
	public PluginFactory<T> getPluginFactory(String key){
		return pluginFactories.get(key);
	}
	
	public Map<String,T> createPlugins(Map<String,Object> attributes){
		Map<String,T> plugins = new LinkedHashMap<>();	
		for(Map.Entry<String,PluginFactory<T>> factory: pluginFactories.entrySet()){
			if(factory.getValue().isEnable()){
				T plugin = factory.getValue().create(attributes);
				if(plugin!=null){
					plugins.put(factory.getKey(), plugin);
				}
			}
		}
		return plugins;
	}
	
	public T createPlugin(String key,Map<String,Object> attributes){
		PluginFactory<T> factory = getPluginFactory(key);
		if(factory!=null && factory.isEnable()){
			T plugin = factory.create(attributes);
			return plugin;
		}
		return null;
	}
	
	
	/**
	 * 获取当前pluginFactory的管理器
	 * @param plugin
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> PluginManager<T> getPluginManager(PluginFactory<T> plugin){
		Class<?> cls = plugin.getClass();
		Type t = cls.getGenericSuperclass();
		Type[] params = ((ParameterizedType) t).getActualTypeArguments();
		Type ncls =  params[0];	
		PluginManager<T>  plugins = (PluginManager<T>)managers.get(ncls);
		if(plugins==null){
			return null;
		}		
		return plugins;
	}	
	
	/**
	 * 获取某个接口类型的的插件管理器，必须提前创建
	 * @param plugin
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> PluginManager<T> getPluginManager(Class<T> pluginInterface){		
		Type ncls =  pluginInterface;	
		PluginManager<T>  plugins = (PluginManager<T>)managers.get(ncls);
		if(plugins==null){
			return null;
		}		
		return plugins;
	}	
	
	public static <T> PluginManager<T> registerPluginFactory(PluginFactory<T> plugin){
		PluginManager<T>  plugins = (PluginManager<T>)getPluginManager(plugin);
		if(plugins==null){
			return null;
		}	
		plugins.getPluginFactories().put(plugin.getKey(), plugin);
		return plugins;
	}	
	
	/**
	 *  重新加载插件，如"D:/plugins/wma-player.jar"
	 */
	public static void reloadPlugin(File file) {		
		
		try {
			// 动态加载插件
			URL pluginJar = file.toURI().toURL();
			URLClassLoader classLoader = new URLClassLoader(
					new URL[] { pluginJar }, Thread.currentThread().getContextClassLoader());
			
			Class<?> clazz = classLoader.loadClass(file.getName());

			// 生成插件实例
			Object obj = clazz.newInstance();

			if (obj instanceof PluginFactory) {
				// 缓存到插件列表中，供之后播放时调用
				PluginManager<?> manager = registerPluginFactory((PluginFactory)obj);
				if(manager==null){
					logger.warn("PluginManager not found for plugin: "+obj);
				}
			}
			
			classLoader.close();
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 扫描插件目录，发现新的重新加载
	 * @throws Exception
	 */
	public static void scanf() {

		// 监控插件目录
		FileAlterationObserver observer = new FileAlterationObserver("plugins/");
		observer.addListener(new FileAlterationListenerAdaptor() {
			@Override
			public void onFileChange(File file) {
				logger.info(file.getName() + " has been changed");
				// 重新加载相应的jar包
				try {
					if(file.getName().endsWith(".class") || file.getName().endsWith(".jar")){
						reloadPlugin(file);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});

		// 设置监控间隔
		FileAlterationMonitor fileMonitor = new FileAlterationMonitor(1000,	observer);

		// 启动监控
		try {
			fileMonitor.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void stopScanf(){
		if(fileMonitor!=null){
			try {
				fileMonitor.stop();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String [] args){
		PluginManager<Runnable> runnableMng = new PluginManager<Runnable>(){
			@Override
			public void init() {
				// TODO Auto-generated method stub
				
			}			
		};
		
		new PluginFactory<Runnable>("test"){
			
			@Override
			public String getName() {
				// TODO Auto-generated method stub
				return "test";
			}

			@Override
			public Runnable create(Map attributes) {
				// TODO Auto-generated method stub
				return new Thread();
			}
		};
		
		PluginManager<Runnable> manager = PluginManager.getPluginManager(Runnable.class);
		PluginFactory<Runnable> factory = manager.getPluginFactory("test");
		Runnable run = factory.create(null);
	}
}
